﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using PaymentHelper.Core;
using PaymentHelper.Core.ExceptionHandler;
using PaymentHelper.PaymentFlow.ModelTemplate;
using PaymentHelper.PaymentFlow.CommonHelper;
using PaymentHelper.PaymentFlow;
using PaymentHelper.Core.Extension;
using PaymentHelper.Core.CommonUtils;

namespace PaymentHelper.PaymentFlow
{
    public abstract class BasePaymentFlow:BaseExtension
    {
        /// <summary>
        /// Utility help to save object
        /// </summary>
        private ITemporaryStorage _tempStorage;

        /// <summary>
        /// Provide handlers to order
        /// </summary>
        protected IOrderHandler OrderHandler
        {
            set
            {
                PaymentFlowManager.OrderHandler = value;
            }
            get
            {
                return PaymentFlowManager.OrderHandler;
            }
        }

        public BasePaymentFlow()
        {
            _tempStorage = new SessionStorage();
        }

        /// <summary>
        /// Order of current payment 
        /// </summary>
        public Order OrderPayment
        {
            get 
            {
                if(_tempStorage.CheckExist("PH_Order") == false) 
                   _tempStorage.SaveObject("PH_Order", new Order());

                return _tempStorage.GetObject("PH_Order") as Order;
            }
            set
            {
                _tempStorage.SaveObject("PH_Order", value);
            }
        }

        /// <summary>
        /// Result of current payment
        /// </summary>
        public PaymentResult PaymentCurrentResult
        {
            get 
            {
                var state = PaymentManager.Instance.GetCurrentPaymentState();
                return state == null ? null : state.Result;
            }
        }

        /// <summary>
        /// Register routes of a BasePaymentFlow
        /// </summary>
        /// <param name="route"></param>
        public abstract void RegisterRoute(RouteCollection route);

        /// <summary>
        /// Get route information about the first step of payment flow
        /// </summary>
        /// <returns></returns>
        public abstract RouteModel GetFirstStepRoute();

        /// <summary>
        /// Get information of route processing payment result
        /// </summary>
        /// <param name="token">tokenPayment of current payment</param>
        /// <returns></returns>
        public abstract RouteModel GetProcessResultRoute(string token);

        /// <summary>
        /// Initialize order information of  payment
        /// </summary>
        public virtual void InitializeOrderPayment()
        {
            OrderPayment = OrderHandler.InitializeOrder();
        }

        /// <summary>
        /// Get information shipping of current payment
        /// </summary>
        /// <param name="frmData">form colletion containing data</param>
        public virtual void GetOrderAddressInfo(FormCollection frmData) 
        {
        }

        /// <summary>
        /// Get list of payment method model
        /// </summary>
        /// <returns></returns>
        public virtual IList<MethodModel> GetListMethodModel()
        {
            var methods = PaymentManager.Instance.LoadAllPaymentMethod(OrderPayment.CustomerCurrency);

            IList<MethodModel> model = new List<MethodModel>();
            foreach (var method in methods)
            {
                model.Add(method.ToPaymentInfoModel());
            }

            return model;
        }

        /// <summary>
        /// Get information of route which gets payment information
        /// </summary>
        /// <returns></returns>
        public virtual RouteModel GetPaymentInfoRoute()
        {
            var method = PaymentManager.Instance.LoadPaymentMethodBySystemName(OrderPayment.PaymentMethodSystemName);

            string action, controller;
            RouteValueDictionary route;
            method.GetPaymentInfoRoute(out action, out controller, out route);

            return new RouteModel() { 
                Action = action,
                Controller = controller,
                RoutePaymentInfo = route
            };
        }

        /// <summary>
        /// Get payment info from user
        /// </summary>
        /// <param name="frmData">form contains data</param>
        /// <returns>
        /// Valid: return null value
        /// Invalid: return list of warning
        /// </returns>
        public virtual IList<string> GetPaymentMethodInfo(FormCollection frmData)
        {
            var method = PaymentManager.Instance.LoadPaymentMethodBySystemName(OrderPayment.PaymentMethodSystemName);
            
            var controller = method.GetControllerType();
            var listError = controller.ValidatePaymentInfo(frmData);

            if(listError == null)
                method.GetPaymentInfo(OrderPayment,frmData);

            return listError;
        }

        /// <summary>
        /// Process a payment request
        /// </summary>
        public virtual void DoPayment()
        {
            if(OrderHandler != null)
                OrderHandler.SaveOrder(OrderPayment);
            
            string token = PaymentManager.Instance.GeneratePaymentKey(OrderPayment.PaymentMethodSystemName);
            
            var processResultRoute = GetProcessResultRoute(token);
            string urlProcess = processResultRoute.ToUrl();
            
            PaymentManager.Instance.UpdatePaymentState(token, urlProcess);

            var processPaymentRequest = new ProcessPaymentRequest()
                {
                    Order = OrderPayment,
                    PaymentMethodSystemName = OrderPayment.PaymentMethodSystemName,
                    UrlProcessResult = urlProcess
                };

            var method = PaymentManager.Instance.LoadPaymentMethodBySystemName(OrderPayment.PaymentMethodSystemName);
            try
            {
                method.ProcessPayment(processPaymentRequest);
            }
            catch (Exception ex)
            {
                throw new PaymentHelperException(ExceptionLevel.Error, "PaymentFlow-DoPayment", ex.ToString());
            }
        }       

        /// <summary>
        /// Update total price of order
        /// </summary>
        public virtual void UpdateTotalOrder()
        {
            var method = PaymentManager.Instance.LoadPaymentMethodBySystemName(OrderPayment.PaymentMethodSystemName);

            var handlingFee = method.GetHandlingFee();
            OrderPayment.UpdateTotalPrice(handlingFee);
        }
    }
}
